package com.bytebuddytest.classcreation;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2023/08/15 14:23:43
 */
public class ByteBuddyRedefineRebaseTest {

    @Test
    public void testGetInstrumentation() {
        ByteBuddyAgent.install();
        Instrumentation instrumentation1 = ByteBuddyAgent.getInstrumentation();
        Instrumentation instrumentation2 = ByteBuddyAgent.getInstrumentation();
        Assertions.assertSame(instrumentation1, instrumentation2);
    }

    @Test
    public void testInstall() {
        Instrumentation instrumentation1 = ByteBuddyAgent.install();
        Instrumentation instrumentation2 = ByteBuddyAgent.install();
        Assertions.assertSame(instrumentation1, instrumentation2);
    }

    @Test
    public void testRedefine() {
        ByteBuddyAgent.install();
        Foo foo = new Foo();
        new ByteBuddy()
                .redefine(Foo.class)
                .method(ElementMatchers.named("bar"))
                .intercept(FixedValue.value("qux"))
                .make()
                .load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

        Assertions.assertEquals(foo.bar(), "qux");
    }

    @Test
    public void testSaveInFile() {
        DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()
                .redefine(Foo.class)
                .method(ElementMatchers.named("bar"))
                .intercept(FixedValue.value("qux"))
                .make();

        saveClassFile(dynamicType);
    }

    private void saveClassFile(DynamicType.Unloaded<?> unloaded) {
        @SuppressWarnings("ConstantConditions")
        String folderPath = getClass().getClassLoader().getResource("").getPath() + "save";
        System.out.println("folderPath = " + folderPath);

        try {
            unloaded.saveIn(new File(folderPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testClassLoader() {
        System.out.println("ClassLoader = " + getClass().getClassLoader());
    }

    @Test
    public void testTypeDescription() throws NoSuchFieldException {
        TypePool typePool = TypePool.Default.ofSystemLoader();
        Class<?> bar = new ByteBuddy()
                .redefine(typePool.describe("com.bytebuddytest.classcreation.Foo").resolve(),
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .defineField("qux", String.class)
                .make()
                .load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                .getLoaded();
        Assertions.assertNotNull(bar.getDeclaredField("qux"));
    }

    @ToString
    static class ToStringTestObject {
        @Override
        public String toString() {
            return "not intercepted";
        }
    }

    @Test
    public void testToStringAgent() {
        String s = new ToStringTestObject().toString();
        System.out.println("toString() = " + s);
        Assertions.assertEquals(s, "transformed");
    }

}
